package me.xuym.md2htm;

import me.xuym.md2htm.constant.Constants;
import me.xuym.md2htm.entity.Args;
import org.fusesource.jansi.Ansi;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by broche on 10/29/16.
 */
public class TranslateAction {
    private Args args;

    //    private CBSDocTranslator translator = new CBSDocTranslator();
    private FlexmarkTranslator translator = new FlexmarkTranslator();


    private File rootOutputFile;
    private int copyCount = 0;
    private int translateCount = 0;
    private int ignoreCount = 0;


    public TranslateAction(MyApplication application) {
        args = ((Args) application.getArgs());
    }

    public void translate() {
        // input path
        File inputFile = args.getInputFile();
        if (!inputFile.exists()) {
            System.out.println(Ansi.ansi().fg(Ansi.Color.RED).a(inputFile.getPath()).a(" is not existed"));
            return;
        }
        if (!inputFile.isDirectory()) {
            System.out.println(Ansi.ansi().fg(Ansi.Color.RED).a(inputFile.getPath()).a(" is not a directory"));
            return;
        }
        System.out.println(Ansi.ansi().fg(Ansi.Color.BLUE).a("Your source path is: ").fg(Ansi.Color.YELLOW).a(inputFile.getPath()));

        // output path
        if (args.getOutputFile() != null) {
            rootOutputFile = args.getOutputFile();
        } else {
            rootOutputFile = new File(inputFile, "out");
        }
        System.out.println(Ansi.ansi().fg(Ansi.Color.BLUE).a("Your output path is: ").fg(Ansi.Color.YELLOW).a(rootOutputFile.getPath()));

        // create css
        System.out.println(Ansi.ansi().fg(Ansi.Color.BLUE).a("Start initing..."));
        if (!createCss(rootOutputFile, args.getStyle())) {
            System.out.println(Ansi.ansi().fg(Ansi.Color.RED).a("Init failed"));
            return;
        }

        // process
        System.out.println(Ansi.ansi().fg(Ansi.Color.BLUE).a("Start transforming..."));
        long startTime = System.currentTimeMillis();
        process(inputFile, rootOutputFile);
        System.out.println();
        System.out.println(Ansi.ansi().fg(Ansi.Color.BLUE).a("Transform success, translated: ")
                .fg(Ansi.Color.YELLOW).a(translateCount)
                .fg(Ansi.Color.BLUE).a(" Copyed: ").fg(Ansi.Color.YELLOW).a(copyCount)
                .fg(Ansi.Color.BLUE).a(" Ignored: ").fg(Ansi.Color.YELLOW).a(ignoreCount)
                .fg(Ansi.Color.BLUE).a(" Time spent: ").fg(Ansi.Color.YELLOW).a(System.currentTimeMillis() - startTime).a("ms"));
    }

    private void process(File inputFile, File outputFile) {
        if (inputFile.getPath().equals(rootOutputFile.getPath())) {
            return;
        } else if (checkIgnore(inputFile, args.getIgnoreList())) {
            if (args.isVerbose()) {
                System.out.println(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Ignore ").fg(Ansi.Color.BLUE).a(inputFile.getPath()));
            }
            ignoreCount++;
            return;
        } else if (inputFile.isDirectory()) {
            File[] files = inputFile.listFiles();
            for (File tinputFile : files) {
                File toutputFile;
                if (tinputFile.isDirectory()) {
                    toutputFile = new File(outputFile, tinputFile.getName());
                } else {
                    if (isMarkdownFile(tinputFile)) {
                        toutputFile = new File(outputFile, tinputFile.getName().substring(0, tinputFile.getName().lastIndexOf('.')) + ".htm");
                    } else {
                        toutputFile = new File(outputFile, tinputFile.getName());
                    }
                }
                process(tinputFile, toutputFile);
            }
        } else {
            if (isMarkdownFile(inputFile)) {
                translateCount++;
                if (args.isVerbose()) {
                    System.out.println(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Translating ").fg(Ansi.Color.BLUE).a(inputFile.getPath()));
                }
                translate(inputFile, outputFile);
            } else {
                copyCount++;
                if (args.isVerbose()) {
                    System.out.println(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Copying ").fg(Ansi.Color.BLUE).a(inputFile.getPath()));
                }
                copy(inputFile, outputFile);
            }
        }
    }


    private boolean createCss(File outputFile, String style) {
        boolean result = true;
        File cssFolder = new File(outputFile, "css");
        cssFolder.mkdirs();

        File cssFile = new File(cssFolder, "mdstyle.css");
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        inputStream = this.getClass().getResourceAsStream("/css/" + style + ".css");
        try {
            if (style.equals("")) {
                inputStream = this.getClass().getResourceAsStream("/css/avenir-white.css");
            } else {
                if (inputStream == null) {
                    inputStream = this.getClass().getResourceAsStream("/css/" + style + ".css");
                }
            }
            fileOutputStream = new FileOutputStream(cssFile);

            byte[] buffer = new byte[128];
            int count;
            while ((count = inputStream.read(buffer)) > 0) {
                fileOutputStream.write(buffer, 0, count);
            }
        } catch (IOException e) {
            e.printStackTrace();
            result = false;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.flush();
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    private void copy(File inputFile, File outputFile) {
        if (!mkdir(outputFile)) {
            System.out.println(Ansi.ansi().fg(Ansi.Color.RED).a(outputFile.getPath() + " create failed"));
            return;
        }

        FileChannel in = null;
        FileChannel out = null;
        FileInputStream inStream = null;
        FileOutputStream outStream = null;
        try {
            inStream = new FileInputStream(inputFile);
            outStream = new FileOutputStream(outputFile);
            in = inStream.getChannel();
            out = outStream.getChannel();
            in.transferTo(0, in.size(), out);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void translate(File inputFile, File outputFile) {
        if (!mkdir(outputFile)) {
            System.out.println(Ansi.ansi().fg(Ansi.Color.RED).a(outputFile.getPath() + " create failed"));
            return;
        }

        BufferedReader reader = null;
        BufferedWriter writer = null;
        try {
            reader = new BufferedReader(new FileReader(inputFile));
            writer = new BufferedWriter(new FileWriter(outputFile));

            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
                stringBuilder.append("\r\n");
            }
            writer.write(Constants.outputHeader);
            writer.write(translator.translate(stringBuilder.toString()));
            writer.write(Constants.outputFooter);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (writer != null) {
                try {
                    writer.flush();
                    writer.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    private boolean mkdir(File outputFile) {
        if (!outputFile.getParentFile().exists()) {
            try {
                boolean res = outputFile.getParentFile().mkdirs();
                if (!res) {
                    return false;
                }
            } catch (Exception e) {
                return false;
            }
        }
        return true;
    }

    private boolean isMarkdownFile(File file) {
        return (file.getName().endsWith(".md") || file.getName().endsWith(".markdown"));
    }

    private boolean checkIgnore(File file, List<String> ignoreList) {
        for (String ignore : ignoreList) {
            Pattern pattern = Pattern.compile(ignore);
            Matcher matcher = pattern.matcher(file.getPath());
            if (matcher.find()) {
                return true;
            }
        }
        return false;
    }
}
